package mka.puzzle.shape;

import java.awt.Graphics;
import java.util.ArrayList;

/***
 * Circle Circle Equation: (x-a)^2 + (y-b)^2 = R^2 center(a,b) R: radius
 * 
 * @version 20131223
 * 
 * @author NguyenBB
 */
public class Circle extends Conic {

	/**
	 * center.
	 * 
	 * Point of Center
	 */
	private Point center;

	/**
	 * radius
	 */
	private double radius;

	/**
	 * pointList.
	 * 
	 * All Point of Circle
	 */
	private ArrayList<Point> pointList;

	/**
	 * Constructor(Circle())
	 */
	public Circle() {
		this.center = new Point();
		this.radius = 0;
		this.pointList = new ArrayList<Point>();
	}

	/**
	 * Constructor(Circle(Point point))
	 * 
	 * @param point
	 *            Point
	 */
	public Circle(Point point) {
		this.center = new Point(point.getX(), point.getY());
		this.radius = 0;
		this.pointList = new ArrayList<Point>();
	}

	/**
	 * Constructor(Circle(Point point, double r))
	 * 
	 * @param point
	 *            (Center of Circle) Point
	 * @param r
	 *            (Radius of Circle) double
	 */
	public Circle(Point point, double r) {
		this.center = new Point(point.getX(), point.getY());
		this.radius = r;
		this.pointList = new ArrayList<Point>();
	}

	/**
	 * Get Point Center
	 * 
	 * @return Point
	 */
	public Point getCenter() {
		return center;
	}

	/**
	 * Set Point Center
	 */
	public void setCenter(Point center) {
		this.center = center;
	}

	/**
	 * getRadius
	 * 
	 * @return double
	 */
	public double getRadius() {
		return radius;
	}

	/**
	 * setRadius
	 */
	public void setRadius(double radius) {
		this.radius = radius;
	}

	/**
	 * getPointList
	 * 
	 * @return ArrayList<Point>
	 */
	public ArrayList<Point> getPointList() {
		return pointList;
	}

	/**
	 * setPointList
	 * 
	 * @param pointList
	 *            ArrayList <Point>
	 */
	public void setPointList(ArrayList<Point> pointList) {
		this.pointList = pointList;
	}

	/***************************************************************
	 * Override from superclass.
	 ***************************************************************/

	/**
	 * getName
	 * 
	 * @return String
	 */
	@Override
	public String getName() {
		return "Circle";
	}

	/**
	 * getPerimeter
	 * 
	 * @return double
	 */
	@Override
	public double getPerimeter() {
		return 2 * Math.PI * this.radius;
	}

	/**
	 * getArea
	 * 
	 * @return double
	 */
	@Override
	public double getArea() {
		return this.radius * this.radius * Math.PI;
	}

	/**
	 * containBound
	 * 
	 * Determine a point belongs the circle
	 * 
	 * @param point
	 *            Point
	 * @return boolean
	 */
	@Override
	public boolean containBound(Point point) {
		if (Math.round(Math.pow(point.getX() - this.center.getX(), 2)
				+ Math.pow(point.getY() - this.center.getY(), 2)) == Math
				.round(this.radius * this.radius)) {
			return true;
		}
		return false;
	}

	/**
	 * containShape
	 * 
	 * Determine a Point on the Circle
	 * 
	 * @param point
	 *            Point
	 * @return boolean
	 */
	@Override
	public boolean containShape(Point p) {
		if (Math.pow(p.getX() - this.center.getX(), 2)
				+ Math.pow(p.getY() - this.center.getY(), 2) <= this.radius
				* this.radius) {
			return true;
		}
		return false;
	}

	/**
	 * estimateYPositive
	 * 
	 * Estimate YAsix (y > 0)
	 * 
	 * @param x
	 *            double
	 * @return double
	 */
	public double estimateYPositive(double x) {
		double yTemp = Math.round(Math.sqrt(Math.pow(this.radius, 2)
				- Math.pow(x - this.center.getX(), 2)))
				+ this.center.getY();
		return yTemp;
	}

	/**
	 * estimateYNegative
	 * 
	 * Estimate YAsix (y < 0)
	 * 
	 * @param x
	 *            double
	 * @return double
	 */
	public double estimateYNegative(double x) {
		double yTemp = Math.round(Math.sqrt(Math.pow(this.radius, 2)
				- Math.pow(x - this.center.getX(), 2)))
				* (-1) + this.center.getY();
		return yTemp;
	}

	/**
	 * allPointBound.
	 * 
	 * Get all point on the circle
	 * 
	 * @return ArrayList
	 */
	@Override
	public ArrayList<Point> allPointBound() {
		// long R = (long) (this.radius + 0.5);
		double R = this.radius;
		// Min value of X
		double minX = this.getCenter().getX() - R;
		// Max value of X
		double maxX = this.getCenter().getX() + R;

		double yTemp;

		double delta = 0.01;
		for (double x = minX; x <= maxX; x = x + delta) {
			yTemp = estimateYNegative(x);
			pointList.add(new Point(x, yTemp));
			yTemp = estimateYPositive(x);
			pointList.add(new Point(x, yTemp));
		}
		return pointList;
	}

	/**
	 * allPointBound2.
	 * 
	 * Equation parameter: x = r * cos(t) y = r * sin(t)
	 * 
	 * @return
	 */
	public ArrayList<Point> allPointBound2() {
		long x, y;
		for (int i = 1; i <= 3600; i++) {
			x = Math.round(this.center.getX() + this.radius * Math.cos(i));
			y = Math.round(this.center.getY() + this.radius * Math.sin(i));
			pointList.add(new Point(x, y));
		}
		return pointList;
	}

	/**
	 * allPointShape.
	 * 
	 * @return ArrayList<Point>
	 */
	@Override
	public ArrayList<Point> allPointShape() {
		double R = this.radius;
		// Min value of X
		double minX = this.getCenter().getX() - R;
		// Max value of X
		double maxX = this.getCenter().getX() + R;
		// Min value of Y
		double minY = this.getCenter().getY() - R;
		// Max value of Y
		double maxY = this.getCenter().getY() + R;

		for (double x = minX; x <= maxX; x++) {
			for (double y = minY; y <= maxY; y++) {
				Point pointTemp = new Point(x, y);
				if (this.containShape(pointTemp) == true) {
					pointList.add(new Point(x, y));
				}
			}
		}
		return pointList;
	}

	/**
	 * Test: Have much time =,=
	 * 
	 * @return
	 */
	public ArrayList<Point> allPointShape2() {
		long R = (long) (this.radius + 0.5);
		// Min value of X
		long minX = (long) this.getCenter().getX() - R;
		// Max value of X
		long maxX = (long) this.getCenter().getX() + R;
		// Min value of Y
		long minY = (long) this.getCenter().getY() - R;
		// Max value of Y
		long maxY = (long) this.getCenter().getY() + R;

		for (long x = minX; x < maxX; x = x + 1) {
			for (long y = minY; y <= maxY; y = y + 1) {
				Point pointTemp = new Point(x, y);
				if (this.containShape(pointTemp) == true) {
					pointList.add(new Point(x, y));
				}
			}
		}
		return pointList;
	}

	/**
	 * allPointShape3.
	 * 
	 * Equation parameter: x = a + r * cos(t); y = b + r * sin(t)
	 * 
	 * @return ArrayList
	 */
	public ArrayList<Point> allPointShape3() {
		double R = this.radius;
		double x, y;
		while (R >= 0) {
			for (double i = 1; i <= 3600; i = i + 1) {
				x = this.center.getX() + R * Math.cos(i);
				y = this.center.getY() + R * Math.sin(i);
				pointList.add(new Point(x, y));
			}
			R = R - 1.75;
		}
		return pointList;
	}

	/***************************************************************
	 * Draw and Paint Object.
	 ***************************************************************/
	/***
	 * draw.
	 * 
	 * Draw the circle by java function
	 * 
	 * @param g Graphics
	 */
	public void draw(Graphics g) {
		g.drawOval((int) (this.getCenter().getX() - this.getRadius()),
				(int) (this.getCenter().getY() - this.getRadius()),
				(int) this.getRadius(), (int) this.getRadius());
	}
}